﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ClassifierLib
{
    /// <summary>
    /// This class represents the conditions to which a rule applies
    /// (similar to allele class from old code)
    /// </summary>
    [Serializable]
    public class Condition : IComparable<Condition>, IComparable
    {
        private List<double> _lowerBounds;
        private List<double> _upperBounds;

        /// <summary>
        /// Default constructor, create empty condition
        /// </summary>
        public Condition()
        {
            _lowerBounds = new List<double>();
            _upperBounds = new List<double>();
        }

        /// <summary>
        /// Create condition from pre-existing lower and upper bounds
        /// </summary>
        /// <param name="LowerBounds"></param>
        /// <param name="UpperBounds"></param>
        public Condition(List<double> LowerBounds, List<double> UpperBounds)
        {
            this.LowerBounds = LowerBounds;
            this.UpperBounds = UpperBounds;
        }

        /// <summary>
        /// Returns true if this rule applies for the given environment state
        /// </summary>
        /// <param name="State">Environment State</param>
        /// <returns></returns>
        public bool Matches(Environment.State State)
        {
            Debug.Assert(State.Values.Count == LowerBounds.Count,"condition vectors should be the same length as state vectors");

            for (int i = 0; i < State.Values.Count; i++)
            {
                if ((State.Values[i] > UpperBounds[i]) || (State.Values[i] < LowerBounds[i]))
                    return false;
                	
            }

            return true;
        }
        
        // Combine Conditions
        public static Condition operator +(Condition c1, Condition c2)
        {
            Debug.Assert(c1.LowerBounds.Count == c2.LowerBounds.Count, "condition vectors should be the same length for combining");

            int count = c1.LowerBounds.Count;

            List<double> newUpperBounds = new List<double>(count);
            List<double> newLowerBounds = new List<double>(count);

            for (int i = 0; i < count; i++)
            {
            	newLowerBounds[i] = Math.Max(c1.LowerBounds[i],c2.LowerBounds[i]);
                newUpperBounds[i] = Math.Max(c1.UpperBounds[i],c2.UpperBounds[i]);
            }
            

            return new Condition(newLowerBounds,newUpperBounds);
        }


        /// <summary>
        /// array of values for lower boundary of accepted region for each value in State
        /// </summary>
        public List<double> LowerBounds
        {
            get
            {
                return _lowerBounds;
            }
            set
            {
                _lowerBounds = value;
            }
        }
        /// <summary>
        /// array of values for upper boundary of accepted region for each value in State
        /// </summary>
        public List<double> UpperBounds
        {
            get
            {
                return _upperBounds;
            }
            set
            {
                _upperBounds = value;
            }
        }

        public override string ToString()
        {
            return DoubleListsToString(LowerBounds, UpperBounds);
        }

        private string DoubleListsToString(List<Double> lowerlist, List<Double> upperlist)
        {
            string result = "[";

            for (int i = 0; i < lowerlist.Count; i++)
            {
                if (lowerlist[i] == upperlist[i])
                    result += lowerlist[i].ToString();
                else
                    result += "#";
            }

            result += "]";

            return result;

        }

        /// <summary>
        /// Is this condition a generalized version of otherCondition
        /// </summary>
        /// <param name="otherCondtion">Condition to test against</param>
        /// <returns>true if this condition is generalized form of otherCondition</returns>
        public bool Generalized(Condition otherCondition)
        {
            bool result = true;
            int boundsLength = this.UpperBounds.Count;
            Debug.Assert(boundsLength == otherCondition.UpperBounds.Count, "vector lengths must match");

            for (int i = 0; i < boundsLength; i++)
            {
                if (otherCondition.LowerBounds[i] == otherCondition.UpperBounds[i])
                {
                    bool match = (LowerBounds[i] <= otherCondition.LowerBounds[i]) && (UpperBounds[i] >= otherCondition.UpperBounds[i]);
                    result &= match;
                }
                else
                {
                    bool match = (LowerBounds[i] == otherCondition.LowerBounds[i]) && (UpperBounds[i] == otherCondition.UpperBounds[i]);
                    result &= match;
                }
           
            }

            return result;
        }

        /// <summary>
        /// Implementing Equals as the base Equals doesn't work!
        /// </summary>
        /// <param name="condition"></param>
        /// <returns></returns>
        public bool Equals(Condition condition)
        {
            //TODO: Add check for same length
            for (int i = 0; i < this._lowerBounds.Count; i++)
            {
                if (this._lowerBounds[i] != condition._lowerBounds[i])
                    return false;
            }
            for (int i = 0; i < this._upperBounds.Count; i++)
            {
                if (this._upperBounds[i] != condition._upperBounds[i])
                    return false;
            }
            return true;
        }

        #region IComparable implementation
        /// <summary>
        /// Implemented to allow sorting in listview
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public int CompareTo(Condition other)
        {
            return ToString().CompareTo(other.ToString());
        }
        #endregion

        #region IComparable Members

        public int CompareTo(object obj)
        {
            return this.CompareTo(obj as Condition);
        }

        #endregion
    }
}
